﻿using System;
using System.IO;

using octalforty.Componento.Core.ObjectModel;

namespace octalforty.Componento.Core.ApplicationServices
{
    public class PackageInstallerService
    {
        public event PackageInstallerEventHandler InstallingPackage = (sender, args) => { };

        public event PackageInstallerEventHandler InstallingResource = (sender, args) => { };

        public PackageResourceProviderService PackageResourceProvider
        { get; set; }

        public DependencyGraphManagerService DependencyGraphManager
        { get; set; }

        public PackageResourceInstallerService PackageResourceInstaller
        { get; set; }

        public void Install(string libPath, PackageManifest package, params Uri[] repositoryUris)
        {
            //
            // First, build up dependency graph and check for potential conflicts between
            // packages to be installed and those already installed
            var dependencyGraph = DependencyGraphManager.BuildDepedencyGraph(package, repositoryUris);

            //
            // When installing, go from terminal nodes upwards. This is done to ensure that 
            // all packages will already have their dependencies installed.
            var packageNode = dependencyGraph.GetNodeFor(package.Name, package.Version);
            InternalInstall(libPath, packageNode);
        }

        private void InternalInstall(string libPath, DependencyGraphNode packageNode)
        {
            if(packageNode.DependentOn.Count > 0)
                foreach(var dependencyNode in packageNode.DependentOn)
                    InternalInstall(libPath, dependencyNode);

            InstallingPackage(this, new PackageInstallerEventArgs(packageNode.Manifest));
            var packageLibPath = Path.Combine(libPath, packageNode.Manifest.Name);

            foreach(var resource in packageNode.Manifest.Resources)
            {
                var providedResources = PackageResourceProvider.GetResourceUris(packageNode.Manifest, resource.Uri);

                foreach(var providedResource in providedResources)
                {
                    InstallingResource(this, new PackageInstallerEventArgs(packageNode.Manifest, providedResource, packageLibPath));

                    PackageResourceInstaller.InstallResource(providedResource, packageLibPath);
                } // foreach
            } // foreach
        }

        public void Uninstall()
        {}
    }

    public class PackageInstallerEventArgs : EventArgs
    {
        public PackageManifest Manifest
        { get; private set; }

        public Uri Resource
        { get; private set; }

        public string TargetDirectory
        { get; private set; }

        public PackageInstallerEventArgs(PackageManifest manifest) :
            this(manifest, null, null)
        {
        }

        public PackageInstallerEventArgs(PackageManifest manifest, Uri resource, string targetDirectory)
        {
            Manifest = manifest;
            Resource = resource;
            TargetDirectory = targetDirectory;
        }
    }

    public delegate void PackageInstallerEventHandler(object sender, PackageInstallerEventArgs args);
}
